/* 
 * shamefully copy from AnFetion.
 * more see: http://code.google.com/p/anfetion/
 */

package person.bangbang.im.Androidgin.Fetion;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SIPCLogin extends SIPCBase {

    private FetionUri fetionid;
    private HashedPasswordParameter passwordParameter;
    private int activeEclipse;
    private String ssic = null;
    private int status = Settings.STATUS_ONLINE;
    // private static final String LOGIN_HEADER_PART1 =
    // "<args><device type=\"PC\" version=\"298096012\" "
    // +
    // "client-version=\"3.4.1280\" /><caps value=\"simple-im;im-session;temp-group;personal-group\" />"
    // + "<events value=\"contact;permission;system-message;personal-group;compact\" />"
    // + "<user-info attributes=\"all\" /><presence><basic value=\"";
    private static final String LOGIN_HEADER_PART1 = "<args><device type=\"PC\" version=\"0\" "
            + "client-version=\"" + Settings.FAKE_VERSION
            + "\" /><caps value=\"simple-im;im-session;temp-group;personal-group;im-relay\" />"
            + "<events value=\"contact;permission;system-message;personal-group;compact\" />"
            + "<user-info attributes=\"all\" /><presence><basic value=\"";
    private static final String LOGIN_HEADER_PART2 = "\" desc=\"\" /></presence></args>";

    // private static final String LOGIN_HEADER = "<args>"
    // + "<device type=\"PC\" version=\"286905224\" client-version=\"3.1.0480\" />"
    // + "<caps value=\"simple-im;im-session;temp-group\" />"
    // + "<events value=\"contact;permission;system-message\" />"
    // + "<user-info attributes=\"all\" /><presence>"
    // + "<basic value=\"400\" desc=\"\" /></presence></args>";

    public void setActiveEclipse(int activeEclipse) {
        this.activeEclipse = activeEclipse;
    }

    public int getActiveEclipse() {
        return activeEclipse;
    }

    public SIPCLogin(FetionUri fetionid, TcpSendAndReceive connection, SerialManager serialManager,
            HashedPasswordParameter passwordParameter, String ssic, int status) {
        super(serialManager, connection);
        // System.out.println(System.currentTimeMillis() / 1000);
        this.fetionid = fetionid;
        this.passwordParameter = passwordParameter;
        this.ssic = ssic;
        this.status = (status == Settings.STATUS_HIDDEN) ? Settings.STATUS_OFFLINE : status;
    }

    @Override
    public boolean execute() {
        this.onStatusChanged(StatusDefine.CONNECTEDTOREMOTE, StatusDefine.LOGINING);

        serialManager.reset();
        ArrayList<FetionSIPC.HeaderPair> headers = new ArrayList<FetionSIPC.HeaderPair>();
        headers.add(new FetionSIPC.HeaderPair("F", this.fetionid.getId()));
        headers.add(new FetionSIPC.HeaderPair("I", "1 "));
        headers.add(new FetionSIPC.HeaderPair("Q", String.valueOf(this.serialManager.getRSerial()) + " R"));
        String body = LOGIN_HEADER_PART1 + status + LOGIN_HEADER_PART2;
        headers.add(new FetionSIPC.HeaderPair("L", StrUtil.getLength(body)));

        FetionSIPC sipc = new FetionSIPC("R fetion.com.cn SIP-C/2.0", headers, body);
        if (this.SendAndReceiveResponse(sipc)) {
            try {
                if (!sipc.response.getResponseCmdLine().code.equals(SIPC_RESPONSE_CODE_UNAUTHRIZED)) {
                    this.onSetText("Return code for logining error.");
                    this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                this.onSetText("Login fail.");
                this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
                return false;
            }
        } else {
            this.onSetText("Time out while logining.");
            this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
            return false;
        }

        Pattern pattern = Pattern.compile("nonce=\"(.*?)\"");
        Matcher matcher;
        try {
            matcher = pattern.matcher(sipc.response.GetHeaderValue("W"));
            if (!matcher.find()) {
                this.onSetText("Can't find nonce in the return field.");
                this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
                return false;
            }
        } catch (Exception e1) {
            e1.printStackTrace();
            return false;
        }
        try {
            String nonce = matcher.group(1);
            String cnonce = FetionHelper.CreateNonce();
            String response = FetionHelper.ComputeResponse(Integer.parseInt(this.fetionid.getId()), nonce,
                    cnonce, this.fetionid.getDomain(), this.passwordParameter.passwordDigest);
            String salt = FetionHelper.Byte2HexString(FetionHelper.toByteArray(this.passwordParameter.salt));

            headers = new ArrayList<FetionSIPC.HeaderPair>();

            headers.add(new FetionSIPC.HeaderPair("F", this.fetionid.getId()));
            headers.add(new FetionSIPC.HeaderPair("I", "1"));
            headers
                    .add(new FetionSIPC.HeaderPair("Q", String.valueOf(this.serialManager.getRSerial())
                            + " R"));
            String ssicadd = ssic == null ? "" : ",ssic=\"" + ssic + "\"";
            headers.add(new FetionSIPC.HeaderPair("A", "Digest algorithm=\"SHA1-sess\",response=\""
                    + response + "\",cnonce=\"" + cnonce + "\",salt=\"" + salt + "\"" + ssicadd));
            // body = login_header;
            headers.add(new FetionSIPC.HeaderPair("L", StrUtil.getLength(body)));
            FetionSIPC loginSipc = new FetionSIPC("R fetion.com.cn SIP-C/2.0", headers, body);

            if (this.SendAndReceiveResponse(loginSipc)) {
                if (!loginSipc.response.getResponseCmdLine().code.equals(SIPC_RESPONSE_CODE_OK)) {
                    this.onSetText("Login fail.");
                    this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
                    return false;
                } else {
                    this
                            .setActiveEclipse(Integer.parseInt(loginSipc.response.GetHeaderValue("X").trim()) * 100);
                    return true;
                }
            } else {
                this.onSetText("Time out while logining.");
                this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
                return false;
            }
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
            this.onSetText("Login fail.");
            this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
            return false;
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
            this.onSetText("Login fail.");
            this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
            this.onSetText("Login fail.");
            this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            this.onSetText("Login fail.");
            this.onStatusChanged(StatusDefine.LOGINING, StatusDefine.CONNECTEDTOREMOTE);
            return false;
        }

    }

    public boolean MakeActive() throws Exception {
        ArrayList<FetionSIPC.HeaderPair> headers = new ArrayList<FetionSIPC.HeaderPair>();
        headers.add(new FetionSIPC.HeaderPair("F", this.fetionid.getId()));
        headers.add(new FetionSIPC.HeaderPair("I", "1"));
        headers.add(new FetionSIPC.HeaderPair("Q", String.valueOf(serialManager.getRSerial()) + " R"));

        String body = "";

        FetionSIPC sipc = new FetionSIPC("R fetion.com.cn SIP-C/2.0", headers, body);
        if (!this.SendAndReceiveResponse(sipc)) {
            return false;
        }
        try {
            // 取出下次激活时间
            this.setActiveEclipse(Integer.parseInt(sipc.response.GetHeaderValue("X").trim()) * 100);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

}
